package top.suyarong.ftbqsync.utils;

import top.suyarong.ftbqsync.FTBQSync;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;

public class ReflectionUtil {

    private static final FTBQSync plugin = FTBQSync.getInstance();

    private static final LogUtil log = LogUtil.getInstance();

    /**
     * 基础反射
     *
     * @param clazzName 类名
     * @return Class
     */
    public static Class<?> getClass(String clazzName) {
        try {
            return Class.forName(clazzName);
        } catch (ClassNotFoundException e) {
            log.error(plugin, "初始化 " + clazzName + " 失败! 该插件只适用于1.12.2");
            e.printStackTrace();
            throw new RuntimeException();
        }
    }

    /**
     * 获取类实例
     *
     * @param clazz 实例
     * @return 实例对象
     * @throws InstantiationException 实例化失败抛出
     */
    public static Object getClassInstance(Class<?> clazz) throws InstantiationException {
        try {
            return clazz.newInstance();
        } catch (IllegalAccessException e) {
            log.error(plugin, "初始化实例 " + clazz.getName() + " 失败! 该插件只适用于1.12.2");
            e.printStackTrace();
            throw new RuntimeException();
        }
    }

    /**
     * 获取方法
     *
     * @param clazz      提取类
     * @param methodName 方法名
     * @return Method
     */
    public static Method getUnknownMethod(Class<?> clazz, String methodName) {
        try {
            return clazz.getDeclaredMethod(methodName);
        } catch (NoSuchMethodException e) {
            log.error(plugin, "初始化方法 " + methodName + " 失败! 该插件只适用于1.12.2");
            e.printStackTrace();
            throw new RuntimeException();
        }
    }

    /**
     * 获取单参数方法
     *
     * @param clazz         方法提取类
     * @param methodName    方法名
     * @param parameterType 方法参数类
     * @return Method
     */
    public static Method getSingleParamMethod(Class<?> clazz, String methodName, Class<?> parameterType) {
        try {
            return Arrays.stream(clazz.getDeclaredMethods())
                    .filter(method -> method.getName().equals(methodName))
                    .filter(method -> method.getParameterCount() == 1 && method.getParameterTypes()[0].isAssignableFrom(parameterType))
                    .findFirst()
                    .orElseThrow(NoSuchMethodException::new);
        } catch (NoSuchMethodException e) {
            log.error(plugin, "初始化方法 " + methodName + " 失败! 该插件只适用于1.12.2");
            e.printStackTrace();
            throw new RuntimeException();
        }
    }

    /**
     * 获取单参构造
     *
     * @param clazz         类
     * @param parameterType 参
     * @return Constructor
     */
    public static Constructor<?> getSingleParamConstructor(Class<?> clazz, Class<?> parameterType) {
        try {
            return Arrays.stream(clazz.getConstructors())
                    .filter(method -> method.getParameterCount() == 1 && method.getParameterTypes()[0].isAssignableFrom(parameterType))
                    .findFirst()
                    .orElseThrow(NoSuchMethodException::new);
        } catch (NoSuchMethodException e) {
            log.error(plugin, "初始化构造失败! 该插件只适用于1.12.2");
            e.printStackTrace();
            throw new RuntimeException();
        }
    }

    /**
     * 获取类变量
     *
     * @param clazz     类
     * @param fieldName 变量名
     * @return Field
     */
    public static Field getClassField(Class<?> clazz, String fieldName) {
        try {
            return Arrays.stream(clazz.getDeclaredFields())
                    .filter(field -> field.getName().equals(fieldName))
                    .findFirst()
                    .orElseThrow(NoSuchMethodException::new);
        } catch (NoSuchMethodException e) {
            log.error(plugin, "初始化参数 " + fieldName + " 失败! 该插件只适用于1.12.2");
            e.printStackTrace();
            throw new RuntimeException();
        }
    }

    /**
     * 方法转换MethodHandle
     *
     * @param method 方法
     * @return MethodHandle
     */
    public static MethodHandle lookup(Method method) {
        try {
            return MethodHandles.lookup().unreflect(method);
        } catch (IllegalAccessException e) {
            log.error(plugin, "初始化方法 " + method.getName() + " 失败! 该插件只适用于1.12.2");
            e.printStackTrace();
            throw new RuntimeException();
        }
    }

    /**
     * 获取静态变量值
     *
     * @param clazz     clazz
     * @param fieldName 变量名
     * @return Object value
     */
    public static Object getStaticFieldValue(Class<?> clazz, String fieldName) {
        try {
            Field declaredField = clazz.getDeclaredField(fieldName);
            declaredField.setAccessible(true);
            return declaredField.get(null);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            log.error(plugin, "初始化变量 " + fieldName + " 失败! 该插件只适用于1.12.2");
            e.printStackTrace();
            throw new RuntimeException();
        }
    }

    /**
     * 获取枚举常量
     *
     * @param clazz     枚举类
     * @param fieldName 枚举常量
     * @return Object value
     */
    public static Object getEnumFieldValue(Class<?> clazz, String fieldName) {
        try {
            Field field = clazz.getField(fieldName);
            return field.get(null);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            log.error(plugin, "初始化枚举常量 " + fieldName + " 失败! 该插件只适用于1.12.2");
            e.printStackTrace();
            throw new RuntimeException();
        }
    }

    /**
     * 获取实例变量值
     *
     * @param object 实例
     * @param field  变量
     * @return Object value
     */
    public static Object getInstanceFieldValue(Object object, Field field) {
        try {
            return field.get(object);
        } catch (IllegalAccessException e) {
            log.error(plugin, "初始化变量 " + field + " 失败! 该插件只适用于1.12.2");
            e.printStackTrace();
            throw new RuntimeException();
        }
    }

    /**
     * 执行方法
     *
     * @param method   方法
     * @param instance 执行实例
     * @param params   参数
     * @return methodReturn
     */
    public static Object invokeMethod(Method method, Object instance, Object... params) {
        try {
            method.setAccessible(true);
            return method.invoke(instance, params);
        } catch (InvocationTargetException | IllegalAccessException e) {
            log.error(plugin, "方法 " + method + " 执行失败! 该插件只适用于1.12.2");
            e.printStackTrace();
            throw new RuntimeException();
        }
    }

    /**
     * 执行静态方法
     *
     * @param method 方法
     * @param params 参数
     * @return methodReturn
     */
    public static Object invokeStaticMethod(Method method, Object... params) {
        try {
            method.setAccessible(true);
            return method.invoke(null, params);
        } catch (InvocationTargetException | IllegalAccessException e) {
            log.error(plugin, "方法 " + method + " 执行失败! 该插件只适用于1.12.2");
            e.printStackTrace();
            throw new RuntimeException();
        }
    }

    /**
     * 实例化对象
     *
     * @param constructor 构造
     * @param params      参数
     * @return 实例
     */
    public static Object newInstanceByConstructor(Constructor<?> constructor, Object... params) {
        try {
            return constructor.newInstance(params);
        } catch (InvocationTargetException | InstantiationException | IllegalAccessException e) {
            log.error(plugin, "实例化对象 " + constructor + " 失败! 该插件只适用于1.12.2");
            e.printStackTrace();
            throw new RuntimeException();
        }
    }

    /**
     * 查找符合条件的方法(未知方法名)
     *
     * @param clazz          被查找的类
     * @param returnType     返回值类型条件
     * @param parameterTypes 入参类型条件
     * @return method
     */
    public static Method findMethod(Class<?> clazz, Class<?> returnType, Class<?>... parameterTypes) {
        return Arrays.stream(clazz.getDeclaredMethods())
                .filter(method -> method.getReturnType() == returnType &&
                        method.getParameterCount() == parameterTypes.length)
                .filter(method -> {
                    Class<?>[] methodParameterTypes = method.getParameterTypes();
                    return Arrays.equals(parameterTypes, methodParameterTypes);
                })
                .findFirst()
                .orElse(null);
    }

    /**
     * 查找符合条件的方法(这个方法的底细我已经全部知道了)
     *
     * @param clazz          被查找的类
     * @param methodName     方法名
     * @param returnType     返回值类型条件
     * @param parameterTypes 入参类型条件
     * @return method
     */
    public static Method findMethod(Class<?> clazz, String methodName, Class<?> returnType, Class<?>... parameterTypes) {
        return Arrays.stream(clazz.getDeclaredMethods())
                .filter(method -> method.getName().equals(methodName))
                .filter(method -> method.getReturnType() == returnType &&
                        method.getParameterCount() == parameterTypes.length)
                .filter(method -> {
                    Class<?>[] methodParameterTypes = method.getParameterTypes();
                    return Arrays.equals(parameterTypes, methodParameterTypes);
                })
                .findFirst()
                .orElse(null);
    }


}
